#ifndef _VL_CONTAINER__H_
#define _VL_CONTAINER__H_

#include <memory>
#include <vector>
#include <unordered_map>
#include <mutex>
#include <string>
#include <functional>

#include "vl_valid.h"
#include "utils/vl_file.h"
#include "utils/vl_buffer.h"

namespace VL {

/**
 * @Author: CimZzz
 * @Date: 2022-08-04 16:06:43
 * 
 * 文件包装器
 */
class FileWrapper: public std::enable_shared_from_this<FileWrapper> {
public:
	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 16:16:10
	 * 
	 * 文件变化枚举
	 */
	enum FileChangedEnum {
		// 未检测
		None,
		// 文件已变化
		Changed,
		// 文件未变化
		NotChanged
	};

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 16:07:50
	 * 
	 * 构造函数
	 * 
	 * @param filePtr 文件指针
	 */
	explicit FileWrapper(VLFile* filePtr);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 16:07:50
	 * 
	 * 构造函数
	 * 
	 * @param filePtr 文件指针
	 */
	explicit FileWrapper(std::shared_ptr<VLFile> filePtr);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 16:10:15
	 * 
	 * 获取文件指针
	 * 
	 * @return 文件指针
	 */
	std::shared_ptr<VLFile> getFilePtr() const;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 23:38:13
	 * 
	 * 获取文件的绝对路径
	 * 
	 * @return 文件绝对路径
	 */
	std::string getAbsolutePath() const;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-19 19:04:38
	 * 
	 * 获取文件的绝对路径
	 * 
	 * @return 文件绝对路径字符串
	 */
	const char * getAbsolutePathStr() const {
		return this->filePtr->getAbsolutePathStr();
	}

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 16:14:35
	 * 
	 * 获取文件最新修改时间
	 * 
	 * @return 文件最新修改时间
	 */
	int64_t getModifyTime();

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 19:29:10
	 * 
	 * 更新文件修改时间
	 * 
	 * @return 返回更新后的修改时间
	 */
	int64_t updateModifyTime();

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 16:17:23
	 * 
	 * 获取文件变化枚举
	 * 
	 * @return 文件变化枚举
	 */
	FileChangedEnum getFileChangeEnum() const;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 12:25:52
	 * 
	 * 判断文件是否发生变化
	 * 
	 * @return 如果文件发生变化返回 ture
	 */
	bool isFileChange();

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 23:59:15
	 * 
	 * 获取文件键值
	 */
	std::string getFileKey() const;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:44:19
	 * 
	 * 获取文件标志
	 * 
	 * @return 文件标志
	 */
	uint32_t getFileFlag() const;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:44:49
	 * 
	 * 判断文件是否包含某个标志
	 * 
	 * @param flag 检查的文件标志
	 * 
	 * @return 如果存在返回 true
	 */
	bool containFlag(uint32_t flag) const;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 16:52:19
	 * 
	 * 获取依赖文件列表
	 * 
	 * @return 依赖文件列表指针
	 */
	std::shared_ptr<std::vector<std::shared_ptr<FileWrapper>>> getDependentFileList() const;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 12:32:33
	 * 
	 * 获取首位依赖文件
	 * 
	 * @return 首位依赖文件
	 */
	std::shared_ptr<FileWrapper> getFirstDependentFile();

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-12 14:14:32
	 * 
	 * 获取校验文件
	 * 
	 * @return 校验文件指针
	 */
	std::shared_ptr<ValidFile> getValidFilePtr();

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-09 14:51:57
	 * 
	 * 将所有依赖文件路径组合成一个字符串
	 * 
	 * @param buffer 字节缓冲区
	 * @param wrapPrefix 路径前缀
	 * @param wrapPostfix 路径后缀
	 * @param separator 分隔符
	 * 
	 * @note 组装好的字符串会输出到字节缓冲区中
	 */
	void composeDependentFiles(Buffer* buffer, const char * wrapPrefix = "\"", const char * wrapPostfix = "\"", const char * separator = " ");

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 16:21:50
	 * 
	 * 设置文件是否发生变化
	 * 
	 * @param isChanged 如果文件发生变化传 true
	 * @param changeHook 触发观察者变化事件回调, 第一个参数是发生变化文件指针, 第二个参数是受影响文件的指针, 如果返回值为 true 的话, 在受影响文件处理变化时也会带入 changeHook 方法
	 */
	void setFileChanged(bool isChanged, std::function<bool(FileWrapper*, FileWrapper*)> changeHook = nullptr);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 23:58:45
	 * 
	 * 设置文件键值
	 * 
	 * @param fileKey 文件价值
	 */
	void setFileKey(const std::string& fileKey);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:39:44
	 * 
	 * 设置文件标志
	 * 
	 * @param flag 文件标志
	 */
	void setFileFlags(uint32_t flag);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:40:18
	 * 
	 * 添加文件标志
	 * 
	 * @param flag 添加的文件标志
	 * 
	 * @note 与现有标志进行按位或操作
	 */
	void addFileFlags(uint32_t flag);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:41:57
	 * 
	 * 删除文件标志
	 * 
	 * @param flag 删除文件标志
	 * 
	 * @note 将参数标志取反, 与现有标志进行按位与操作
	 */
	void delFileFlags(uint32_t flag);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 16:36:10
	 * 
	 * 增加监控文件
	 * 
	 * @param fileWrapper 被监控文件
	 * 
	 * @note 监控文件, 如果被监控文件需要更新, 则推导本文件同样需要改变 
	 */
	void monitorFile(std::shared_ptr<FileWrapper> fileWrapper);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 16:49:11
	 * 
	 * 添加依赖文件
	 * 
	 * @param fileWrapper 依赖文件
	 * 
	 * @note 依赖文件可以看做是本文件的组成部分, 会自动添加到监控文件列表中
	 */
	void addDependentFile(std::shared_ptr<FileWrapper> fileWrapper);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-12 14:15:02
	 * 
	 * 安装校验文件
	 * 
	 * @param validFile 校验文件
	 */
	void setupValidFile(ValidFile* validFile);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-22 16:03:44
	 * 
	 * 设置额外信息
	 * 
	 * @param extraInfo 额外信息指针
	 * 
	 * @note 注意, FileWrapper 只是持有数据指针, 并非智能指针!
	 */
	void setExtraInfo(void* extraInfo) {
		this->extraInfo = extraInfo;
	}

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-22 16:04:40
	 * 
	 * 获取额外信息
	 * 
	 * @return 转换后的额外信息指针
	 * 
	 * @note 需要自行保证指针类型是否正确
	 */
	template<typename T> T* getExtraInfo() {
		return static_cast<T*>(this->extraInfo);
	}

private:
	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 16:42:30
	 * 
	 * 添加到观察者列表中
	 * 
	 * @param observer 观察者
	 */
	void addObserver(std::shared_ptr<FileWrapper> observer);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 16:17:05
	 * 
	 * 文件变化枚举
	 */
	FileChangedEnum changeEnum;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:39:06
	 * 
	 * 文件标志位
	 * 可以根据不同作用自定义标志位
	 */
	uint32_t flag;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 22:59:59
	 * 
	 * 线程锁
	 */
	std::mutex mutex;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 16:18:09
	 * 
	 * 缓存的文件修改时间
	 */
	int64_t modifyTime;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 23:58:25
	 * 
	 * 文件键值
	 */
	std::string fileKey;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 16:08:19
	 * 
	 * 文件指针
	 */
	std::shared_ptr<VLFile> filePtr;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-12 14:13:53
	 * 
	 * 校验文件指针
	 */
	std::shared_ptr<ValidFile> validFilePtr;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 16:13:50
	 * 
	 * 目标文件列表
	 */
	std::shared_ptr<std::vector<std::shared_ptr<FileWrapper>>> observerFileList;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 16:28:24
	 * 
	 * 依赖文件列表
	 */
	std::shared_ptr<std::vector<std::shared_ptr<FileWrapper>>> dependentFileList;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-22 16:03:08
	 * 
	 * 额外信息
	 */
	void* extraInfo;
};

/**
 * @Author: CimZzz
 * @Date: 2022-08-04 16:59:40
 * 
 * 文件池
 * 防止文件重复创建
 */
class FilePool {
public:
	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-29 18:41:23
	 * 
	 * 文件生成回调
	 */
	using FetchCallback = std::function<std::shared_ptr<FileWrapper>()>;
	
	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 17:00:26
	 * 
	 * 构造函数
	 */
	explicit FilePool();

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-29 18:41:44
	 * 
	 * 获取文件
	 * 
	 * @param sourceKey 文件键值
	 * @param callback 如果文件不存在, 则会调用此回调来生成文件实例
	 * @param absolutePathAsKey 文件全路径额外作为键值
	 * 
	 * @return 文件
	 */
	std::shared_ptr<FileWrapper> fetchFile(const std::string& sourceKey, FetchCallback callback, bool absolutePathAsKey = false);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-29 18:41:44
	 * 
	 * 获取文件, 同步控制
	 * 
	 * @param sourceKey 文件键值
	 * @param callback 如果文件不存在, 则会调用此回调来生成文件实例
	 * @param absolutePathAsKey 文件全路径额外作为键值
	 * 
	 * @return 文件
	 */
	std::shared_ptr<FileWrapper> fetchFileSync(const std::string& sourceKey, FetchCallback callback, bool absolutePathAsKey = false);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 22:32:21
	 * 
	 * 获取文件列表指针
	 * 
	 * @return 文件列表指针
	 */
	std::vector<std::shared_ptr<FileWrapper>>* getFileList();

private:
	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 17:05:58
	 * 
	 * 线程锁
	 */
	std::mutex mutex;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-29 18:06:48
	 * 
	 * 文件键值表
	 */
	std::unordered_map<std::string, std::shared_ptr<FileWrapper>> fileMap;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-04 22:31:55
	 * 
	 * 文件列表
	 */
	std::vector<std::shared_ptr<FileWrapper>> fileList;
};

/**
 * @Author: CimZzz
 * @Date: 2022-08-08 17:35:18
 * 
 * 自定义数据收集对儿
 */
template<typename P, typename R>
struct CustomDataCollectorPair {
	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:22:12
	 * 
	 * 主要数据
	 */
	std::shared_ptr<P> primaryData = std::shared_ptr<P>(nullptr);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:22:18
	 * 
	 * 关联数据列表
	 */
	std::shared_ptr<std::vector<std::shared_ptr<R>>> relateData = std::shared_ptr<std::vector<std::shared_ptr<R>>>(nullptr);
};

/**
 * @Author: CimZzz
 * @Date: 2022-08-08 17:35:47
 * 
 * 自定义数据收集器
 */
template<typename P, typename R>
class CustomDataCollector {
public:
	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:14:08
	 * 
	 * 构造函数
	 */
	explicit CustomDataCollector() {

	}

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:23:03
	 * 
	 * 保存主要数据
	 * 
	 * @param key 数据键值
	 * @param dataPtr 主要数据指针
	 */
	void savePrimaryData(const std::string& key, const std::shared_ptr<P>& dataPtr) {
		typename std::shared_ptr<R> otherPtr(nullptr);
		this->saveData(key, dataPtr, otherPtr);
	}

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:24:19
	 * 
	 * 保存关联数据
	 * 
	 * @param key 数据键值
	 * @param dataPtr 关联数据指针
	 */
	void saveRelateData(const std::string& key, const std::shared_ptr<R>& dataPtr) {
		typename std::shared_ptr<P> otherPtr(nullptr);
		this->saveFile(key, otherPtr, dataPtr);
	}

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:25:00
	 * 
	 * 保存数据
	 * 
	 * @param key 数据键值
	 * @param primaryData 主要数据指针
	 * @param relateData 关联数据指针
	 */
	void saveData(const std::string& key, const std::shared_ptr<P>& primaryData, const std::shared_ptr<R>& relateData) {
		std::lock_guard<std::mutex> locker(this->mutex);
		auto iter = this->dataMap.find(key);
		typename std::shared_ptr<CustomDataCollectorPair<P, R>> pairPtr;
		if(iter != this->dataMap.end()) {
			// 找到对象
			pairPtr = iter->second;
		} else {
			pairPtr = std::shared_ptr<CustomDataCollectorPair<P, R>>(new CustomDataCollectorPair<P, R>());
			this->dataMap.emplace(key, pairPtr);
		}

		if(pairPtr->primaryData.get() == nullptr && primaryData.get() != nullptr) {
			pairPtr->primaryData = primaryData;
		}

		if(relateData.get() != nullptr) {
			if(pairPtr->relateData.get() == nullptr) {
				pairPtr->relateData = std::shared_ptr<std::vector<std::shared_ptr<R>>>(new std::vector<std::shared_ptr<R>>);
			}
			pairPtr->relateData->push_back(relateData);
		}
	}

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 16:16:03
	 * 
	 * 判断当前数据搜集器是否为空
	 * 
	 * @return 如果为空返回 true
	 */
	bool empty() const {
		return this->dataMap.empty();
	}

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 16:17:03
	 * 
	 * 返回当前数据搜集器数据尺寸
	 * 
	 * @return 数据搜集器数据尺寸
	 */
	uint32_t getSize() const {
		return this->dataMap.size();
	}

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:29:37
	 * 
	 * 获取数据搜集器 Map 的起始迭代器
	 * 
	 * @return Map 的起始迭代器
	 */
	typename std::unordered_map<std::string, std::shared_ptr<CustomDataCollectorPair<P, R>>>::iterator begin() {
		return this->dataMap.begin();
	}

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:29:37
	 * 
	 * 获取数据搜集器 Map 的终止迭代器
	 * 
	 * @return Map 的终止迭代器
	 */
	typename std::unordered_map<std::string, std::shared_ptr<CustomDataCollectorPair<P, R>>>::iterator end() {
		return this->dataMap.end();
	}

private:
	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:26:23
	 * 
	 * 线程锁
	 */
	std::mutex mutex;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-29 18:06:48
	 * 
	 * 数据键值表
	 */
	typename std::unordered_map<std::string, std::shared_ptr<CustomDataCollectorPair<P, R>>> dataMap;
};

/**
 * @Author: CimZzz
 * @Date: 2022-08-05 14:19:14
 * 
 * 文件搜集对儿
 */
struct FileCollectorPair {
	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:22:12
	 * 
	 * 主要文件
	 */
	std::shared_ptr<FileWrapper> primaryFile = std::shared_ptr<FileWrapper>(nullptr);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:22:18
	 * 
	 * 关联文件列表
	 */
	std::shared_ptr<std::vector<std::shared_ptr<FileWrapper>>> relateFile = std::shared_ptr<std::vector<std::shared_ptr<FileWrapper>>>(nullptr);
};

/**
 * @Author: CimZzz
 * @Date: 2022-08-05 14:13:06
 * 
 * 文件搜集器
 */
class FileCollector {
public:
	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:14:08
	 * 
	 * 构造函数
	 */
	explicit FileCollector();

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:23:03
	 * 
	 * 保存主要文件
	 * 
	 * @param key 文件键值
	 * @param filePtr 主要文件指针
	 */
	void savePrimaryFile(const std::string& key, const std::shared_ptr<FileWrapper>& filePtr);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:24:19
	 * 
	 * 保存关联文件
	 * 
	 * @param key 文件键值
	 * @param filePtr 关联文件指针
	 */
	void saveRelateFile(const std::string& key, const std::shared_ptr<FileWrapper>& filePtr);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:25:00
	 * 
	 * 保存文件
	 * 
	 * @param key 文件键值
	 * @param primaryFile 主要文件指针
	 * @param relateFile 关联文件指针
	 */
	void saveFile(const std::string& key, const std::shared_ptr<FileWrapper>& primaryFile, const std::shared_ptr<FileWrapper>& relateFile);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 16:16:03
	 * 
	 * 判断当前文件搜集器是否为空
	 * 
	 * @return 如果为空返回 true
	 */
	bool empty() const;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 16:17:03
	 * 
	 * 返回当前文件搜集器数据尺寸
	 * 
	 * @return 文件搜集器数据尺寸
	 */
	uint32_t getSize() const;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:29:37
	 * 
	 * 获取文件搜集器 Map 的起始迭代器
	 * 
	 * @return Map 的起始迭代器
	 */
	std::unordered_map<std::string, std::shared_ptr<FileCollectorPair>>::iterator begin();

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:29:37
	 * 
	 * 获取文件搜集器 Map 的终止迭代器
	 * 
	 * @return Map 的终止迭代器
	 */
	std::unordered_map<std::string, std::shared_ptr<FileCollectorPair>>::iterator end();

private:
	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-05 14:26:23
	 * 
	 * 线程锁
	 */
	std::mutex mutex;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-07-29 18:06:48
	 * 
	 * 文件键值表
	 */
	std::unordered_map<std::string, std::shared_ptr<FileCollectorPair>> fileMap;
};

/**
 * @Author: CimZzz
 * @Date: 2022-08-22 08:47:13
 * 
 * 文件列表漫步者
 */
class FileWrapperWalker {
public:
	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-22 08:48:19
	 * 
	 * 构造函数
	 * 
	 * @param fileWrapperListPtr 文件列表指针
	 */
	explicit FileWrapperWalker(std::vector<std::shared_ptr<FileWrapper>>* fileWrapperListPtr);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-22 08:50:54
	 * 
	 * 确保文件分组存在
	 * 
	 * @param count 分组数
	 */
	void makeSureGroupExist(int count);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-22 08:51:46
	 * 
	 * 获取文件列表指针
	 * 
	 * @return 文件列表指针
	 */
	const std::vector<std::shared_ptr<FileWrapper>>* getFileListPtr() const;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-22 13:36:22
	 * 
	 * 获取文件组数量
	 * 
	 * @return 文件组数量
	 */
	unsigned int getGroupCount() const;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-22 08:53:56
	 * 
	 * 获取文件组指针
	 * 
	 * @param groupIdx 组下标
	 * 
	 * @return 文件组指针
	 */
	std::shared_ptr<std::vector<std::shared_ptr<FileWrapper>>> getGroupPtr(unsigned int groupIdx);

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-22 08:52:39
	 * 
	 * 将文件保存至指定组
	 * 
	 * @param groupIdx 组下标
	 * @param filePtr 文件指针
	 */
	void saveFileAt(unsigned int groupIdx, const std::shared_ptr<FileWrapper>& filePtr);

private:
	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-22 08:48:51
	 * 
	 * 文件列表指针
	 */
	std::vector<std::shared_ptr<FileWrapper>>* fileWrapperListPtr;

	/**
	 * @Author: CimZzz
	 * @Date: 2022-08-22 08:50:07
	 * 
	 * 文件分组
	 */
	std::vector<std::shared_ptr<std::vector<std::shared_ptr<FileWrapper>>>> fileGroup;
};

}

#endif // _VL_CONTAINER__H_
